
//Copyright 1997-2009 Syrinx Development, Inc.
//This file is part of the Syrinx Web Application Framework (SWAF).
// == BEGIN LICENSE ==
//
// Licensed under the terms of any of the following licenses at your
// choice:
//
//  - GNU General Public License Version 3 or later (the "GPL")
//    http://www.gnu.org/licenses/gpl.html
//
//  - GNU Lesser General Public License Version 3 or later (the "LGPL")
//    http://www.gnu.org/licenses/lgpl.html
//
//  - Mozilla Public License Version 1.1 or later (the "MPL")
//    http://www.mozilla.org/MPL/MPL-1.1.html
//
// == END LICENSE ==
using System;
using System.Collections;
using System.Diagnostics;
using System.Threading;

using Swaf.BizRule;

namespace Swaf.Instrumentation
{
	/// <summary>
	/// Summary description for RuleMonitor.
	/// </summary>
	public class PerformanceCounterRuleMonitor : IRuleMonitor
	{
		protected string m_catagoryName;
		protected string m_description;
		protected int m_executingRulesCount=0;
		protected int m_enqueuedRulesCount=0;
		protected AvgInfo m_generalRuleExecutionAvg;
		protected IDictionary m_activeRulesets;
		protected IDictionary m_activeRules;
		protected IDictionary m_averageRuleExecutions;
		protected IDictionary m_counters;

		protected PerformanceCounterCategory m_counterCatagory;

		public PerformanceCounterRuleMonitor(string catagoryName, string description)
		{
			m_catagoryName = catagoryName;
			m_description = description;
			m_activeRules = new Hashtable();
			m_activeRulesets = new Hashtable();
			m_averageRuleExecutions = new Hashtable();
			m_counters = new Hashtable();
			m_generalRuleExecutionAvg = new AvgInfo();
			
			setupBasePerformanceCounters();
		}

		public void ruleEnqueued(DateTime enqueueTime, int callId, bool isNewRuleset, string ruleName, object callParams)
		{
			Interlocked.Increment(ref m_enqueuedRulesCount);
			((PerformanceCounter)m_counters["CurrentNumEnqueuedRules"]).RawValue = m_enqueuedRulesCount;
		}

		public void ruleCompleted(DateTime completeTime, int callId, string ruleName)
		{
			Interlocked.Decrement(ref m_executingRulesCount);
			((PerformanceCounter)m_counters["CurrentNumExecutingRules"]).RawValue = m_executingRulesCount;
			calcAvg(completeTime, callId, ruleName);
			lock(m_activeRules)
			{
				m_activeRules.Remove(callId);
			}
		}

		public void ruleFailed(DateTime failTime, int callId, string ruleName, Exception errInfo)
		{
			Interlocked.Decrement(ref m_executingRulesCount);
			((PerformanceCounter)m_counters["CurrentNumExecutingRules"]).RawValue = m_executingRulesCount;
			calcAvg(failTime, callId, ruleName);
			lock(m_activeRules)
			{
				m_activeRules.Remove(callId);
			}
		}

		public void ruleStarted(DateTime startTime, int callId, string ruleName, object callParams)
		{
			Debug.Assert(m_enqueuedRulesCount > 0);
			Interlocked.Decrement(ref m_enqueuedRulesCount);
			((PerformanceCounter)m_counters["CurrentNumEnqueuedRules"]).RawValue = m_enqueuedRulesCount;

			Interlocked.Increment(ref m_executingRulesCount);
			((PerformanceCounter)m_counters["CurrentNumExecutingRules"]).RawValue = m_executingRulesCount;
			lock(m_activeRules)
			{
				m_activeRules[callId] = startTime;
			}
		}

		protected void setupBasePerformanceCounters()
		{
			//if(PerformanceCounterCategory.Exists(m_catagoryName))
			//	PerformanceCounterCategory.Delete(m_catagoryName);

			if(!PerformanceCounterCategory.Exists(m_catagoryName))
			{
				
				CounterCreationDataCollection CCDC = new CounterCreationDataCollection();
			    
				// Add the general avg.
				CounterCreationData averageGeneralRule = new CounterCreationData();
				averageGeneralRule.CounterType = PerformanceCounterType.AverageTimer32;
				averageGeneralRule.CounterName = "AverageRuleExecutionTime";
				CCDC.Add(averageGeneralRule);

				averageGeneralRule = new CounterCreationData();
				averageGeneralRule.CounterType = PerformanceCounterType.AverageBase;
				averageGeneralRule.CounterName = "AverageRuleExecutionTimeBase";
				CCDC.Add(averageGeneralRule);

				CounterCreationData currentNumRules = new CounterCreationData();
				currentNumRules.CounterType = PerformanceCounterType.NumberOfItems32;
				currentNumRules.CounterName = "CurrentNumExecutingRules";
				CCDC.Add(currentNumRules);

				currentNumRules = new CounterCreationData();
				currentNumRules.CounterType = PerformanceCounterType.NumberOfItems32;
				currentNumRules.CounterName = "CurrentNumEnqueuedRules";
				CCDC.Add(currentNumRules);
			    
				// Create the category.
				PerformanceCounterCategory.Create(m_catagoryName, m_description, CCDC);
			}

			m_counterCatagory = new PerformanceCounterCategory(m_catagoryName);
			PerformanceCounter p = new PerformanceCounter(m_catagoryName, "AverageRuleExecutionTime", false);
			p.RawValue = 0;
			m_counters["AverageRuleExecutionTime"] = p;
			p = new PerformanceCounter(m_catagoryName, "AverageRuleExecutionTimeBase", false);
			p.RawValue = 0;
			m_counters["AverageRuleExecutionTimeBase"] = p;

			p = new PerformanceCounter(m_catagoryName, "CurrentNumExecutingRules", false);
			p.RawValue = 0;
			m_counters["CurrentNumExecutingRules"] = p;
			p = new PerformanceCounter(m_catagoryName, "CurrentNumEnqueuedRules", false);
			p.RawValue = 0;
			m_counters["CurrentNumEnqueuedRules"] = p;
		}

		protected void calcAvg(DateTime t, int callId, string ruleName)
		{
			lock(m_generalRuleExecutionAvg)
			{
				long ticks = t.Ticks - ((DateTime)m_activeRules[callId]).Ticks;

				++m_generalRuleExecutionAvg.NumExecutions;
				m_generalRuleExecutionAvg.TotalExecutions += ticks;
				long avg =  m_generalRuleExecutionAvg.AvgExecutionTime;
				((PerformanceCounter)m_counters["AverageRuleExecutionTime"]).IncrementBy(ticks);
				((PerformanceCounter)m_counters["AverageRuleExecutionTimeBase"]).Increment();

				AvgInfo i = (AvgInfo)m_averageRuleExecutions[ruleName];
				if(i == null)
				{
					i = new AvgInfo();
					m_averageRuleExecutions[ruleName] = i;
				}

				++i.NumExecutions;
				i.TotalExecutions += ticks;
			}
		}

		protected class AvgInfo
		{
			public int NumExecutions=0;
			public long TotalExecutions=0;

			public AvgInfo()
			{
				NumExecutions = 0;
				TotalExecutions = 0;
			}

			public long AvgExecutionTime {get{return TotalExecutions/NumExecutions;}}
		}
	}
}
